#include "threadpool.h"

#include <stdlib.h>
#include <unistd.h>

int thread_id = 0;

/**
 * @brief threadpool_run boucle de message du thread
 * @param threadpool
 * @return valeur de retour de la fonction executée
 */
static void* threadpool_run(void* threadpool)
{
    ThreadPool* _threadpool = (ThreadPool*) threadpool;
    Delegate* event;
    int my_id = ++thread_id;

    /*Tant que le nombre de thread n'est pas à zero,
    l'ordre de terminer l'execution n'a pas été recue.*/
    while(_threadpool->count > 0)
    {
        /*On prend le mutex pour lire dans la liste*/
        pthread_mutex_lock(&_threadpool->mutex);
        if(_threadpool->events.count > 0)
        {
            /*On récupère le premier événement arrivé dans la liste*/
            event = (Delegate*) list_getFirstValue(&_threadpool->events);
            /*On supprime cet événement de la liste*/
            list_removeAt(&_threadpool->events,0);
            /*On a lu dans la liste, on libère le mutex*/
            pthread_mutex_unlock(&_threadpool->mutex);

            fprintf(stdout,"execution d'une fonction sur le thread n°%d\n",my_id);
            fflush(stdout);
            /*On execute la fonction contenue dans l'évenement en lui passant ses arguments*/
            event->function(event->args);
            /*On détruit le délégué*/
            delegate_destroy(&event);
        }
        else
        {
            /*On a lu dans la liste, on libère le mutex*/
            pthread_mutex_unlock(&_threadpool->mutex);
            /*On attend une miliseconde pour éviter de consommer des ressources CPU*/
            usleep(10);
        }
    }
    return NULL;
}


void threadpool_create(ThreadPool** threadpool, int count)
{
    (*threadpool) = (ThreadPool*) malloc(sizeof(ThreadPool));
    threadpool_initialize((*threadpool),count);
}

void threadpool_initialize(ThreadPool* threadpool, int count)
{
    int i;
    dynamicArray_initialize(&threadpool->threads);
    list_initialize(&threadpool->events);
    threadpool->count = count;
    /*On initialise le mutex*/
    pthread_mutex_init(&threadpool->mutex,NULL);
    /*On créer et lance le nombre de threads souhaités*/
    for(i = 0; i < count; i++)
    {
        /*On alloue un structure décrivant un thread*/
        pthread_t* thread = (pthread_t*) malloc(sizeof(pthread_t));
        /*On initialise et lance le thread*/
        pthread_create(thread,NULL,threadpool_run, threadpool);
        /*On ajoute le thread à la liste*/
        dynamicArray_inserLast(&threadpool->threads,(void*) thread);

        fprintf(stdout,"thread %d crée\n",i);
        fflush(stdout);
    }
}

void threadpool_clear(ThreadPool* threadpool)
{
    int i, count;
    pthread_t* thread;
    void* ret;

    /*On stock le nombre de thread*/
    count = threadpool->count;
    /*On défini le nombre de thread à 0, ce qui a pour effet
    de quitter la boucle de traitement des événement dans
    les threads. Cela permet de pouvoir quitter proprement
    les threads dès qu'il on terminé l'action courante,
    sans tenir compte d'éventuelle action arrivée entre temps.*/
    threadpool->count = 0;

    /*On prend le mutex pour vider la liste des événements a executer en attente*/
    pthread_mutex_lock(&threadpool->mutex);
    /*On vide la liste des événements a executer en attente*/
    list_clear(&threadpool->events);
    /*On libère le mutex*/
    pthread_mutex_unlock(&threadpool->mutex);
    /*On détruit le mutex*/
    pthread_mutex_destroy(&threadpool->mutex);

    /*On quitte proprement chaques threads, puis on les libèrent*/
    for(i = 0; i < count; i++)
    {
        /*On obtient le thread à l'index courant dans le tableau dynamique*/
        thread = (pthread_t*) threadpool->threads.array[0];
        /*On attend proprement la fin du thread*/
        pthread_join(*thread, &ret);
        /*On supprime le thread de la liste*/
        dynamicArray_removeAt(&threadpool->threads,0);
        /*On libère la structure thread alouée dans le threadpool_initialize*/
        free(thread);
    }
    /*On vide la tableau dynamique de thread*/
    dynamicArray_clear(&threadpool->threads);
}

void threadpool_destroy(ThreadPool** threadpool)
{
    threadpool_clear((*threadpool));
    free((*threadpool));
    (*threadpool) = NULL;
}

void threadpool_execute(ThreadPool* threadpool, Delegate **event)
{
    /*On prend le mutex pour ecrire dans la liste*/
    pthread_mutex_lock(&threadpool->mutex);
    /*On insère l'évenement à exécuter dans la liste*/
    list_insertLast(&threadpool->events, (void*) (*event));

    fprintf(stdout,"ajout d'une fonction à exécuter. file d'attente : %d\n",threadpool->events.count);
    fflush(stdout);

    /*On a ecrit dans la liste, on libère le mutex*/
    pthread_mutex_unlock(&threadpool->mutex);
}
